package order

import (
	"context"
	"encoding/json"
	"strconv"
	"strings"
	"time"

	"sale-admin/config/mysql"
	"sale-admin/config/redis"
	"sale-admin/internal/app/grpc/client"
	"sale-admin/internal/app/grpc/protoc/devicec"
	"sale-admin/internal/app/grpc/protoc/mini_app"
	"sale-admin/internal/app/grpc/protoc/system_admin"
	"sale-admin/internal/app/web/schema"
	"sale-admin/internal/app/web/service/common"
	"sale-admin/internal/dal"
	"sale-admin/internal/dao"
	"sale-admin/internal/define"
	"sale-admin/internal/models"

	"192.168.1.75/go-pkg/errorx"
	"192.168.1.75/go-pkg/helper"
	"192.168.1.75/go-pkg/logx"
	"github.com/golang-module/carbon"
	"github.com/samber/lo"
	"github.com/shopspring/decimal"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
	"gorm.io/hints"
)

type OrderService struct{}

// 需要禁用货道的错误码
var ErrorCodeArr = []string{"2", "9", "22", "26", "35", "36", "37", "38"}

type RefundTotalCount struct {
	TotalSalesCount        int     `gorm:"column:total_sales_count" json:"total_sales_count"`
	TotalSalesAmount       float64 `gorm:"column:total_sales_amount" json:"total_sales_amount"`
	RefundTotalSalesCount  int     `gorm:"column:refund_total_sales_count" json:"refund_total_sales_count"`
	RefundTotalSalesAmount float64 `gorm:"column:refund_total_sales_amount" json:"refund_total_sales_amount"`
}

type OrderList struct {
	models.MaOrder
	UserID            string                    `gorm:"column:user_id" json:"user_id"`
	UserName          string                    `gorm:"column:user_name" json:"user_name"`
	RefundRemarks     string                    `gorm:"column:refund_remarks" json:"refund_remarks"`
	Reason            string                    `gorm:"column:reason" json:"reason"`
	RefundUserID      string                    `gorm:"column:refund_user_id" json:"refund_user_id"`
	RefundUserName    string                    `gorm:"-" json:"refund_user_name"`
	RefundTime        string                    `gorm:"column:refund_time" json:"refund_time"`
	ShopName          string                    `gorm:"-" json:"shop_name"`
	GoodsName         string                    `gorm:"-" json:"goods_name"`
	Univalent         float64                   `gorm:"-" json:"univalent"`
	ContainerNum      string                    `gorm:"-" json:"container_num"`
	ClassName         string                    `gorm:"-" json:"class_name"`
	OriginaImagePath  string                    `gorm:"-" json:"origina_image_path"`
	InputImagePath    string                    `gorm:"-" json:"input_image_path"`
	DiscountType      string                    `gorm:"-" json:"discount_type"`       // 优惠类型
	PickupCode        string                    `gorm:"-" json:"pickup_code"`         // 取货码
	PickupAt          int64                     `gorm:"-" json:"pickup_at"`           // 取货时间
	OrderShipmentInfo *models.OrderShipmentInfo `gorm:"-" json:"order_shipment_info"` // 发货信息
}

type NewOrderList struct {
	models.MaOrder
	UserID           string  `gorm:"column:user_id" json:"user_id"`
	UserName         string  `gorm:"column:user_name" json:"user_name"`
	RefundRemarks    string  `gorm:"column:refund_remarks" json:"refund_remarks"`
	Reason           string  `gorm:"column:reason" json:"reason"`
	RefundUserID     string  `gorm:"column:refund_user_id" json:"refund_user_id"`
	RefundUserName   string  `gorm:"-" json:"refund_user_name"`
	RefundTime       string  `gorm:"column:refund_time" json:"refund_time"`
	ShopName         string  `gorm:"-" json:"shop_name"`
	GoodsName        string  `gorm:"-" json:"goods_name"`
	Univalent        float64 `gorm:"-" json:"univalent"`
	ContainerNum     string  `gorm:"-" json:"container_num"`
	ClassName        string  `gorm:"-" json:"class_name"`
	OriginaImagePath string  `gorm:"-" json:"origina_image_path"`
	InputImagePath   string  `gorm:"-" json:"input_image_path"`
	DiscountType     string  `gorm:"-" json:"discount_type"` // 优惠类型
	// 下面2个是新增的
	IsVisit     int `gorm:"-" json:"is_visit"`     // is_visit 	 是否参与抽奖 1 抽奖 	 2 未参与
	PrizeResult int `gorm:"-" json:"prize_result"` // prize_result 抽奖出货结果 1 出货成功 2 出货失败

	// 新增几个进去
	ProvinceId   int    `json:"province_id"`
	CityId       int    `json:"city_id"`
	LocationID   int    `json:"location_id"`
	DistrictId   int    `json:"district_id"`
	LocationName string `json:"location_name"`
}

// 抽奖订单明细 --- 需要关联订单
func (o OrderService) LotteryOrderList(
	ctx context.Context, page, limit int,
	// 活动ID
	activityID,
	// 省市区商场
	province_id, city_id, district_id, location_id string,
	//商户列表
	shopIDList []string,
	//机器ID
	robotID string,
	//开始\结束时间
	startTime, endTime int,
	//订单号、商品名称
	outTradeNo, goodsName string,
	orderStatus string,
	paymentID string,
	merchantID string,
) (
	data []NewOrderList, count int64, err error,
) {
	db := mysql.NewDB()
	// 订单导出锁：限制100毫秒，不成功就返回
	// if limit > 200 {
	// 	rdx := redis.NewDB()
	// 	success, err := rdx.SetNX(ctx, define.RedisExportLock+userID+":order", 1, 100*time.Millisecond).Result()
	// 	if !success || err != nil {
	// 		return nil, 0, RefundTotalCount{}, errorx.New("导出操作频繁~", -1)
	// 	}
	// }
	tx := db.Table(models.MaOrder{}.TableName() + " as o")
	// 订单状态筛选，支持多个条件
	if orderStatus != "" {
		// 新增状态检索： 检索支付成功或者退款但是状态未结束的订单 (order_status ==2 或者 ==6) and  end ==0
		if orderStatus == "1001" {
			// 注：这个状态只能单独搜索
			tx.Where("o.order_status in (?) and o.end = ?", "2,6", 0)
		} else {
			tx.Where("o.order_status in (?)", strings.Split(orderStatus, ","))
		}
	}
	// 商户筛选
	var merchantIDs []string
	merchantIDs, err = common.Order{}.GetMerchantChildren(ctx, merchantID, shopIDList)
	if err != nil {
		return
	}
	tx.Where("o.merchant_id in (?)", merchantIDs)

	// 日期检索
	if startTime != 0 && endTime != 0 {

		start := int64(startTime)
		end := int64(endTime)
		// 将时间戳转换为 time.Time 类型
		// 将 time.Time 类型转换为日期格式
		startTime := time.Unix(start, 0).Format("2006-01-02")
		endTime := time.Unix(end, 0).Format("2006-01-02")

		tx.Where("o.add_time >= ? and o.add_time <= ?", startTime, endTime+" 23:59:59")
	}

	// 机器 --- 获取活动下面运行参与的机器
	allowRobotIDs, _ := dao.ObjAssociation{}.GetAssociationID(context.Background(), dal.Q, define.AssociationLotteryRobot, activityID, "")
	if len(allowRobotIDs) > 0 {
		tx.Where("o.robot_name IN (?)", allowRobotIDs)
	}

	// -------  上面这里需要测试验证 -------

	// 如果检索的机器名称不为空
	if robotID != "" {
		tx.Where("o.robot_name IN (?)", strings.Split(robotID, ","))
	}

	//支付方式
	if paymentID != "" {
		tx.Where("o.payment_id IN (?)", strings.Split(paymentID, ","))
	}

	//检索活动
	if activityID != "" {
		tx.Where("o.lottery_activity_id = ?", activityID)
	}

	//订单号
	if outTradeNo != "" {
		tx.Where("(o.out_trade_no like ? OR o.child_out_trade_no like ?)", outTradeNo+"%", outTradeNo+"%")
	}

	// 商品名称查询
	var connectOrders []string
	if goodsName != "" {
		goodsIDs := dao.Goods{}.GetGoodsIDByName(context.Background(), dal.Q, goodsName)
		tx = tx.Where("o.goods_id IN ?", goodsIDs)
	}

	// 地区筛选
	if province_id != "" {
		tx.Where("o.areas1 in (?)", strings.Split(province_id, ","))
	}
	if city_id != "" {
		tx.Where("o.areas2 in (?)", strings.Split(city_id, ","))
	}
	if location_id != "" {
		tx.Where("o.areas3 in (?)", strings.Split(location_id, ","))
	}

	// 新建会话，共享条件 ---- 1
	tx1 := tx.Session(&gorm.Session{})

	// 分页查询主订单
	var orderIDs []string
	helper.Gorm{}.
		Paginate(tx1, page, limit).
		Clauses(func() clause.Expression { // 强制使用索引
			if len(merchantIDs) > 0 { // 商户索引
				if db.Migrator().HasIndex(&models.MaOrder{}, "idx_aomdp") {
					return hints.ForceIndex("idx_aomdp")
				}
				return nil
			} else if startTime != 0 && endTime != 0 { // 时间索引更好用
				if db.Migrator().HasIndex(&models.MaOrder{}, "AddTime") {
					return hints.ForceIndex("AddTime")
				}
				return nil
			} else if outTradeNo != "" || len(connectOrders) > 0 { // 订单号索引
				if db.Migrator().HasIndex(&models.MaOrder{}, "OutTradeNoIndex") {
					return hints.ForceIndex("OutTradeNoIndex")
				}
				return nil
			} else { // 默认用时间索引
				if db.Migrator().HasIndex(&models.MaOrder{}, "AddTime") {
					return hints.ForceIndex("AddTime")
				}
				return nil
			}
		}()).
		Order("o.add_time desc,`o`.order_id desc").
		Group("o.out_trade_no").
		Pluck("o.out_trade_no", &orderIDs)

	tx1.Select("count(distinct out_trade_no)").
		Clauses(func() clause.Expression {
			if len(merchantIDs) > 0 {
				if db.Migrator().HasIndex(&models.MaOrder{}, "idx_aomdp") {
					return hints.ForceIndex("idx_aomdp")
				}
				return nil
			} else if startTime != 0 && endTime != 0 {
				if db.Migrator().HasIndex(&models.MaOrder{}, "AddTime") {
					return hints.ForceIndex("AddTime")
				}
				return nil
			} else if paymentID != "" {
				if db.Migrator().HasIndex(&models.MaOrder{}, "PaymentID") {
					return hints.ForceIndex("PaymentID")
				}
				return nil
			} else {
				if db.Migrator().HasIndex(&models.MaOrder{}, "OutTradeNoIndex") {
					return hints.ForceIndex("OutTradeNoIndex")
				}
				return nil
			}
		}()).Take(&count)

	// 新建会话，共享连表条件 ------ 2
	tx2 := tx1.Joins("left join " + models.MaMerchantConfig{}.TableName() + " as user on o.merchant_id = user.id").
		Joins("left join " + models.MaOrderRefund{}.TableName() + " as rf on o.out_trade_no = rf.out_trade_no and o.updated_at = rf.created_at"). // 多商品需要关联子订单，历史数据会没子订单。
		Session(&gorm.Session{})

	// 真正的记录（子订单记录）
	orderList := []NewOrderList{}
	field := "o.*, user.id as user_id, user.user_name, rf.remarks as refund_remarks, rf.reason, rf.add_time as refund_time, rf.refund_user_id"
	tx2.Where("o.out_trade_no IN ?", orderIDs).
		Order("o.add_time desc,`o`.order_id desc").
		Select(field).
		Find(&orderList)

	// 初始化GRPC
	deviceClient, clientErr := client.GetDeviceAdminClient()
	if clientErr != nil {
		logx.Error(context.Background(), "grpc device client error", logx.Err(clientErr))
		return
	}

	// GRPC获取商场位置列表
	deviceReq := &devicec.LocationListReq{}
	deviceRes, err := deviceClient.LocationList(context.Background(), deviceReq)
	LocationMap := make(map[string]string)
	if deviceRes != nil && deviceRes.Code == 0 && err == nil {
		for _, v := range deviceRes.List {
			LocationMap[v.Id] = v.Name
		}
	}
	// 获取积分类型
	var PointsOrderIDs []string
	for _, v := range orderList {
		if v.PaymentID == 9 {
			PointsOrderIDs = append(PointsOrderIDs, v.OutTradeNo)
		}
	}
	OrderPointsList, _ := dao.OrderPoints{}.List(ctx, db, strings.Join(PointsOrderIDs, ","), 1)

	OrderPointsMap := make(map[string]string)
	for _, v := range OrderPointsList {
		if _, ok := define.PointsType[v.PointsID]; ok {
			OrderPointsMap[v.OutTradeNo] = define.PointsType[v.PointsID]
		}
	}

	var outTradeNoIDList []string
	for _, v := range orderList {
		outTradeNoIDList = append(outTradeNoIDList, v.OutTradeNo, v.ChildOutTradeNo)
	}
	// 获取退款的2张图片
	var ErrorLogList []models.MaRobotErrorLogPallets
	var ErrorLogMap = make(map[string]models.MaRobotErrorLogPallets)
	db.Model(&models.MaRobotErrorLogPallets{}).
		Where("out_trade_no in (?)", outTradeNoIDList).
		Group("out_trade_no"). // 同一订单只取最后一次的图片
		Order("add_time desc").
		Find(&ErrorLogList)
	for _, v := range ErrorLogList {
		ErrorLogMap[v.OutTradeNo] = v
	}
	// 转换成标注的正确时间格式
	for k, v := range orderList {
		addTimeFormated, _ := models.DateTime(v.AddTime).MarshalDateTime()
		orderList[k].AddTimeFormated = string(addTimeFormated)
		modifyTimeFormated, _ := models.DateTime(v.ModifyTime).MarshalDateTime()
		orderList[k].ModifyTimeFormated = string(modifyTimeFormated)
		var countData []schema.OrderGoodsDetailOld
		json.Unmarshal([]byte(v.CountData), &countData)
		if len(countData) > 0 {
			orderList[k].GoodsName = countData[0].GoodsName
			orderList[k].ContainerNum = countData[0].ContainerNum
			orderList[k].Univalent = countData[0].Univalent
			orderList[k].ClassName = ""
		}

		if v.Areas3 != 0 {
			AreasID := strconv.Itoa(v.Areas3)
			if _, ok := LocationMap[AreasID]; ok {
				orderList[k].ShopName = LocationMap[AreasID]
			}
		}

		if v.Reason == "0" || v.Reason == "1" || v.Reason == "2" || v.Reason == "3" {
			orderList[k].Reason = ""
		}
		if ErrorLogInfo, ok := ErrorLogMap[v.OutTradeNo]; ok {
			orderList[k].OriginaImagePath = ErrorLogInfo.InputImagePath
			orderList[k].InputImagePath = ErrorLogInfo.OriginaImagePath
		} else if ErrorLogInfo, ok := ErrorLogMap[v.ChildOutTradeNo]; ok {
			orderList[k].OriginaImagePath = ErrorLogInfo.InputImagePath
			orderList[k].InputImagePath = ErrorLogInfo.OriginaImagePath
		}
		if v.PaymentID == 9 {
			if _, ok := OrderPointsMap[v.OutTradeNo]; ok {
				orderList[k].DiscountType = OrderPointsMap[v.OutTradeNo]
			}
		}

		// 通过订单表的IS_VISIT_Lottery 判断是否参与了抽奖
		if v.IsVisitLottery == 1 {
			orderList[k].IsVisit = 1
		} else {
			orderList[k].IsVisit = 2
		}

		// 判断当前订单是否抽奖
		var lotteryOrderInfo = models.MaLotteryOrder{}
		db.Model(&models.MaLotteryOrder{}).
			Where("old_out_trade_no = ?", v.OutTradeNo).
			Where("activity_id = ?", activityID).
			Find(&lotteryOrderInfo)

			// 返回当前订单的抽奖信息
		if lotteryOrderInfo.OutTradeNo != "" {
			// orderList[k].IsVisit = 1 // 参与了抽奖
			if lotteryOrderInfo.OrderStatus == 4 {
				orderList[k].PrizeResult = 1 // 出货成功
			} else {
				orderList[k].PrizeResult = 2 // 出货失败
			}
		} else {
			// orderList[k].IsVisit = 2     // 未参与
			orderList[k].PrizeResult = 0 // 未参与
		}
		// 别名赋值
		orderList[k].ProvinceId = v.Areas1
		orderList[k].CityId = v.Areas2
		orderList[k].LocationID = v.Areas3
		orderList[k].DistrictId = v.Areas5
		if v.Areas3 != 0 {
			AreasID := strconv.Itoa(v.Areas3)
			if _, ok := LocationMap[AreasID]; ok {
				orderList[k].LocationName = LocationMap[AreasID]
			}
		}

	}
	return orderList, count, nil
}

// OrderList 订单列表
func (o OrderService) OrderList(ctx context.Context, db *gorm.DB, merchantID, userID string, page, limit int,
	salesID string, shopIDList []string, payStatus int, orderStatus string, refundType *int,
	goodsName, classId, startTime, endTime, paymentID, outTradeNo, areas1, areas2, areas3, areas5 string, withTotal bool,
) (data []OrderList, count int64, totalCount RefundTotalCount, err error) {

	logx.Error(ctx, "Order List Sales Admin", logx.Any("salesID", salesID))
	// 订单导出锁：限制100毫秒，不成功就返回
	if limit > 200 {
		rdx := redis.NewDB()
		success, err := rdx.SetNX(ctx, define.RedisExportLock+userID+":order", 1, 100*time.Millisecond).Result()
		if !success || err != nil {
			return nil, 0, RefundTotalCount{}, errorx.New("导出操作频繁~", -1)
		}
	}

	tx := db.Table(models.MaOrder{}.TableName() + " as o")

	// 订单状态筛选，支持多个条件
	if orderStatus != "" {
		// 新增状态检索： 检索支付成功或者退款但是状态未结束的订单 (order_status ==2 或者 ==6) and  end ==0
		if orderStatus == "1001" {
			// 注：这个状态只能单独搜索
			tx.Where("o.order_status in (?) and o.end = ?", "2,6", 0)
		} else {
			tx.Where("o.order_status in (?)", strings.Split(orderStatus, ","))
		}
	}

	// 退款类型筛选
	if refundType != nil {
		if *refundType == 0 { // 手动退款
			tx.Where("(o.remarks <> '出货失败_退款成功') or (o.remarks <> '出货失败_退款成功<商品已出弹簧>') or (o.remarks <> '出货失败_退款，保存失败')")
		} else if *refundType == 1 { // 自动退款
			tx.Where("o.remarks like '%出货失败%'")
		} else {
			err = errorx.New("退款类型错误", -1)
			return
		}
	}

	// 商户筛选
	if merchantID != "1" || len(shopIDList) != 0 {
		var merchantIDs []string
		merchantIDs, err = common.Order{}.GetMerchantChildren(ctx, merchantID, shopIDList)
		if err != nil {
			return
		}
		tx.Where("o.merchant_id in (?)", merchantIDs)
	}

	// 日期检索
	if startTime != "" && endTime != "" {
		tx.Where("o.add_time >= ? and o.add_time <= ?", startTime, endTime+" 23:59:59")
	}

	// 机器
	if salesID != "" {
		tx.Where("o.sales_id IN (?)", strings.Split(salesID, ","))
	}

	//支付方式
	if paymentID != "" {
		tx.Where("o.payment_id IN (?)", strings.Split(paymentID, ","))
	}
	//订单号
	if outTradeNo != "" {
		tx.Where("(o.out_trade_no like ? OR o.child_out_trade_no like ?)", outTradeNo+"%", outTradeNo+"%")
	}
	// 商品名称查询
	if goodsName != "" {
		goodsIDs := dao.Goods{}.GetGoodsIDByName(ctx, dal.Q, goodsName)
		tx.Where("o.goods_id IN ?", goodsIDs)
	}
	// 省
	if areas1 != "" {
		tx.Where("o.areas1 in (?)", strings.Split(areas1, ","))
	}
	// 市
	if areas2 != "" {
		tx.Where("o.areas2 in (?)", strings.Split(areas2, ","))
	}
	// 商场
	if areas3 != "" {
		tx.Where("o.areas3 in (?)", strings.Split(areas3, ","))
	}
	// 区县
	if areas5 != "" {
		tx.Where("o.areas5 in ?", strings.Split(areas5, ","))
	}

	// 新建会话，共享条件 ---- 1
	tx1 := tx.Session(&gorm.Session{})

	var TotalCount = RefundTotalCount{}
	if withTotal {
		// 成功订单统计
		var SuccessOrderTotal struct {
			TotalSalesCount  int     `gorm:"column:total_sales_count" json:"total_sales_count"`
			TotalSalesAmount float64 `gorm:"column:total_sales_amount" json:"total_sales_amount"`
		}
		if orderStatus == "4" {
			tx1.Select("count(order_id) as total_sales_count, sum(payment_amount) as total_sales_amount").
				Take(&SuccessOrderTotal)
		}
		// 退款订单统计
		var RefundOrderTotal struct {
			RefundTotalSalesCount  int     `gorm:"column:refund_total_sales_count" json:"refund_total_sales_count"`
			RefundTotalSalesAmount float64 `gorm:"column:refund_total_sales_amount" json:"refund_total_sales_amount"`
		}
		if orderStatus == "6" {
			tx1.Select("count(order_id) as refund_total_sales_count, sum(payment_amount) as refund_total_sales_amount").
				Take(&RefundOrderTotal)
		}
		TotalCount.TotalSalesCount = SuccessOrderTotal.TotalSalesCount
		TotalCount.TotalSalesAmount = SuccessOrderTotal.TotalSalesAmount
		TotalCount.RefundTotalSalesCount = RefundOrderTotal.RefundTotalSalesCount
		TotalCount.RefundTotalSalesAmount = RefundOrderTotal.RefundTotalSalesAmount
	}

	// 分页查询主订单
	var orderIDs []string
	helper.Gorm{}.
		Paginate(tx1, page, limit).
		Order("o.add_time desc,`o`.out_trade_no desc").
		Group("o.add_time,o.out_trade_no").
		Pluck("o.out_trade_no", &orderIDs)

	tx1.Select("count(distinct out_trade_no)").Take(&count)

	// 新建会话，共享连表条件 ------ 2
	tx2 := tx1.Joins("left join " + models.MaMerchantConfig{}.TableName() + " as user on o.merchant_id = user.id").
		Joins("left join " + models.MaOrderRefund{}.TableName() + " as rf on o.out_trade_no = rf.out_trade_no and o.updated_at = rf.created_at"). // 多商品需要关联子订单，历史数据会没子订单。
		Session(&gorm.Session{})

	// 真正的记录（子订单记录）
	orderList := []OrderList{}
	field := "o.*, user.id as user_id, user.user_name, rf.remarks as refund_remarks, rf.reason, rf.add_time as refund_time, rf.refund_user_id"
	tx2.Where("o.out_trade_no IN ?", orderIDs).
		Order("o.out_trade_no desc").
		Select(field).
		Find(&orderList)

	// pickup_at取货时间	pickup_code取货码
	for k, v := range orderList {
		if v.OrderSource == 1 { // 线上
			record, err := dao.CouponChildCode{}.Take(ctx, dal.Q, 0, "", v.OutTradeNo)
			if err == nil && record != nil {
				orderList[k].PickupCode = record.Code
				if record.Status == int(define.CouponCodeStatus_Used) {
					orderList[k].PickupAt = record.UpdatedAt
				}
			}
		}
	}
	// 初始化GRPC
	deviceClient, clientErr := client.GetDeviceAdminClient()
	if clientErr != nil {
		logx.Error(context.Background(), "grpc device client error", logx.Err(clientErr))
		return
	}

	// GRPC获取商场位置列表
	deviceReq := &devicec.LocationListReq{}
	deviceRes, err := deviceClient.LocationList(context.Background(), deviceReq)
	LocationMap := make(map[string]string)
	if deviceRes != nil && deviceRes.Code == 0 && err == nil {
		for _, v := range deviceRes.List {
			LocationMap[v.Id] = v.Name
		}
	}
	// 获取积分类型
	var PointsOrderIDs []string
	for _, v := range orderList {
		if v.PaymentID == 9 {
			PointsOrderIDs = append(PointsOrderIDs, v.OutTradeNo)
		}
	}
	OrderPointsList, _ := dao.OrderPoints{}.List(ctx, db, strings.Join(PointsOrderIDs, ","), 1)

	OrderPointsMap := make(map[string]string)
	for _, v := range OrderPointsList {
		if _, ok := define.PointsType[v.PointsID]; ok {
			OrderPointsMap[v.OutTradeNo] = define.PointsType[v.PointsID]
		}
	}

	var outTradeNoIDList []string
	for _, v := range orderList {
		outTradeNoIDList = append(outTradeNoIDList, v.OutTradeNo, v.ChildOutTradeNo)
	}
	// 获取退款的2张图片
	var ErrorLogList []models.MaRobotErrorLogPallets
	var ErrorLogMap = make(map[string]models.MaRobotErrorLogPallets)
	db.Model(&models.MaRobotErrorLogPallets{}).
		Where("out_trade_no in (?)", outTradeNoIDList).
		Group("out_trade_no"). // 同一订单只取最后一次的图片
		Order("out_trade_no desc").
		Find(&ErrorLogList)
	for _, v := range ErrorLogList {
		ErrorLogMap[v.OutTradeNo] = v
	}
	// 转换成标注的正确时间格式
	for k, v := range orderList {
		addTimeFormated, _ := models.DateTime(v.AddTime).MarshalDateTime()
		orderList[k].AddTimeFormated = string(addTimeFormated)
		modifyTimeFormated, _ := models.DateTime(v.ModifyTime).MarshalDateTime()
		orderList[k].ModifyTimeFormated = string(modifyTimeFormated)
		var countData []schema.OrderGoodsDetailOld
		json.Unmarshal([]byte(v.CountData), &countData)
		if len(countData) > 0 {
			orderList[k].GoodsName = countData[0].GoodsName
			orderList[k].ContainerNum = countData[0].ContainerNum
			orderList[k].Univalent = countData[0].Univalent
			orderList[k].ClassName = ""
		}

		if v.Areas3 != 0 {
			AreasID := strconv.Itoa(v.Areas3)
			if _, ok := LocationMap[AreasID]; ok {
				orderList[k].ShopName = LocationMap[AreasID]
			}
		}

		if v.Reason == "0" || v.Reason == "1" || v.Reason == "2" || v.Reason == "3" {
			orderList[k].Reason = ""
		}
		if ErrorLogInfo, ok := ErrorLogMap[v.OutTradeNo]; ok {
			orderList[k].OriginaImagePath = ErrorLogInfo.InputImagePath
			orderList[k].InputImagePath = ErrorLogInfo.OriginaImagePath
		} else if ErrorLogInfo, ok := ErrorLogMap[v.ChildOutTradeNo]; ok {
			orderList[k].OriginaImagePath = ErrorLogInfo.InputImagePath
			orderList[k].InputImagePath = ErrorLogInfo.OriginaImagePath
		}
		if v.PaymentID == 9 {
			if _, ok := OrderPointsMap[v.OutTradeNo]; ok {
				orderList[k].DiscountType = OrderPointsMap[v.OutTradeNo]
			}
		}
		if v.OrderSource == 1 {
			orderShipmentInfo := dao.OrderUserAddress{}.Detail(ctx, mysql.NewDB(), v.OutTradeNo)
			if orderShipmentInfo != nil {
				orderList[k].OrderShipmentInfo = orderShipmentInfo
			}
		}
	}
	return orderList, count, TotalCount, nil
}

type RefundOrderList struct {
	GoodsName        string  `json:"goods_name"`
	OutTradeNo       string  `json:"out_trade_no"`
	ChildOutTradeNo  string  `json:"child_out_trade_no"`
	AddTime          string  `json:"add_time"`
	ModifyTime       string  `json:"modify_time"`
	PaymentAmount    float64 `json:"payment_amount"`
	PaymentID        int8    `json:"payment_id"`
	UserName         string  `json:"user_name"`
	UserID           string  `json:"user_id"`
	RobotName        string  `json:"robot_name"`
	RobotID          string  `json:"robot_id"`
	SalesID          string  `json:"sales_id"`
	ShopName         string  `json:"shop_name"`
	Univalent        float64 `json:"univalent"`
	ContainerNum     string  `json:"container_num"`
	SalesNum         int     `json:"sales_num"`
	Remarks          string  `json:"remarks"`
	Reason           string  `json:"reason"`
	RefundTime       string  `json:"refund_time"`
	RefundUserID     string  `json:"refund_user_id"`
	RefundUserName   string  `json:"refund_user_name"`
	OrderStatus      int     `json:"order_status"`
	OriginaImagePath string  `json:"origina_image_path"`
	InputImagePath   string  `json:"input_image_path"`
	ErrorCode        string  `json:"error_code"`
}

// GetRefundAnalyzeOfMerchant 以商户为分组，查询每个商户的退款数量、退款金额、退款原因
func (o OrderService) GetRefundAnalyzeOfMerchant(
	ctx context.Context,
	db *gorm.DB,
	merchantID, userID string,
	page, limit int,
	salesID string,
	shopIDList []string,
	startTime, endTime,
	areas1, areas2, areas3 string,
) (
	data []OrderList,
	err error,
) {

	// 查询退款表

	return
}

// RefundOrderList 退款订单列表
func (o OrderService) RefundOrderList(ctx context.Context, merchantID, userID string, page, limit int, salesID string, shopIDList []string,
	payStatus int, orderStatus string, refundType *int, goodsName, classId, startTime, endTime, paymentID,
	outTradeNo, areas1, areas2, areas3, areas5 string,
) (res []RefundOrderList, count int64, totalCount RefundTotalCount, err error) {
	// 从订单列表获取信息
	db := mysql.NewDB()
	data, count, totalCount, err1 := o.OrderList(ctx, db, merchantID, userID, page, limit, salesID, shopIDList, payStatus, orderStatus,
		refundType, goodsName, classId, startTime, endTime, paymentID, outTradeNo, areas1, areas2, areas3, areas5, true)
	if err1 != nil {
		err = err1
		return
	}
	// grpc获取当前商户下的所有用户列表，用于查询退款人名称
	SystemAdminClient, clientErr := client.GetSystemAdminClient()
	if clientErr != nil {
		err = clientErr
		return
	}
	var req system_admin.UserListReq
	userListResult, err1 := SystemAdminClient.UserList(ctx, &req)
	if err1 != nil && userListResult.Code != 0 {
		logx.Error(ctx, "userListResult error", logx.Any("userListResult", userListResult), logx.Any("err", err1.Error()))
		return
	}
	var userNameList = make(map[string]string)
	for _, v := range userListResult.List {
		userNameList[v.Id] = v.Username
	}

	resData := []RefundOrderList{}
	for _, v := range data {
		var item RefundOrderList
		countData := common.Order{}.CountDataOldToNew(v.CountData)
		if len(countData) > 0 {
			item.GoodsName = countData[0].GoodsName
		}
		item.ErrorCode = v.ErrorCode
		if v.PaymentID == 6 {
			item.OutTradeNo = v.EndOutTradeNo
		} else {
			item.OutTradeNo = v.OutTradeNo
		}

		item.OutTradeNo = v.OutTradeNo
		item.ChildOutTradeNo = v.ChildOutTradeNo
		addTimeFormated, _ := models.DateTime(v.AddTime).MarshalDateTime()
		item.AddTime = string(addTimeFormated)
		modifyTimeFormated, _ := models.DateTime(v.ModifyTime).MarshalDateTime()
		item.ModifyTime = string(modifyTimeFormated)
		item.PaymentID = v.PaymentID // 0-微信 1-支付宝 2-支付宝人脸 3-刷卡 4-好邻居 5-兑换码
		item.PaymentAmount = v.PaymentAmount
		item.UserName = v.UserName
		item.UserID = v.UserID
		item.RobotName = v.RobotName
		item.SalesID = v.SalesID
		item.ShopName = v.ShopName
		item.Univalent = v.Univalent
		item.ContainerNum = v.ContainerNum
		item.SalesNum = 1
		item.Remarks = v.Remarks
		item.Reason = v.Reason
		item.RefundTime = v.RefundTime
		item.OrderStatus = v.OrderStatus

		// 人工退款原因显示 -- add in 2022-02-11
		if item.Reason == "0" || item.Reason == "1" || item.Reason == "2" || item.Reason == "3" {
			item.Reason = ""
		}
		RefundRemarks := ""
		if v.RefundRemarks != "" {
			RefundRemarks = "(退款原因:" + v.RefundRemarks + ")"
		}
		item.Remarks = v.Remarks + RefundRemarks
		item.RefundUserID = v.RefundUserID
		if v.RefundUserID != "" && userNameList[v.RefundUserID] != "" {
			item.RefundUserName = userNameList[v.RefundUserID]
		}
		// 出货失败时，显示上报的错误信息 -- add in 2022-02-10
		RefundResult := "" // 定义退款原因
		if v.OrderStatus == 6 || v.OrderStatus == 5 {
			// （ 获取退款操作指南 ）： 先从本订单表获取，再从错误表获取
			if v.ErrorCode == "" {
				// var ErrorInfo models.MaRobotErrorLog
				// db.Model(&models.MaRobotErrorLog{}).
				// 	Where("out_trade_no = ?", v.OutTradeNo).
				// 	Order("add_time desc").
				// 	Take(&ErrorInfo)
				// if ErrorInfo.ErrorCode != "" {
				// RefundResult = getRefundMethod(ErrorInfo.ErrorCode)
				// } else {
				// 细分逻辑： 如果是自动退款，正常会有错误码上报，如果是手动退款，则没有错误码上报
				// 自动退款
				if v.Remarks != "出货失败_退款成功" && v.Remarks != "出货失败_退款成功<商品已出弹簧>" && v.Remarks != "出货失败_退款，保存失败" && v.Remarks != "支付页面 订单结束" {
					// 0-网络异常订单支付未出货 1-货品出错 2-商品质量问题 3-其他
					switch v.Remarks {
					case "0":
						v.Remarks = "网络异常订单支付未出货"
					case "1":
						v.Remarks = "货品出错"
					case "2":
						v.Remarks = "商品质量问题"
					case "3":
						v.Remarks = "其他"
					}
					RefundResult = "【手动退款】：" + v.Remarks
				} else if v.Remarks == "支付页面 订单结束" {
					// 超时支付或者卡点支付
					RefundResult = "用户超时支付或取消出货"
				}
				// }
			} else {
				// - 多商品出货失败，后续商品暂停出货，完善退款原因.
				if v.ErrorCode == "0" && v.ChildOutTradeNo != "" {
					// 判断是否属于多商品出货失败，后续未出货的情况
					myorderData := []models.MaOrder{}
					myorderDataCount := db.Model(&models.MaOrder{}).
						Where("out_trade_no = ?", v.OutTradeNo).
						Where("error_code > ?", 0).
						Order("out_trade_no desc").
						Find(&myorderData).RowsAffected
					if myorderDataCount > 0 {
						RefundResult = "多商品出货失败，后续商品暂停出货"
					}
				}
				if RefundResult == "" {
					RefundResult = getRefundMethod(v.ErrorCode)
				}
			}
			if RefundResult == "" {
				RefundResult = "未知异常"
			}
			if len(countData) > 0 {
				item.Remarks = countData[0].ContainerNum + "货道_" + RefundResult
			}
		}

		// 获取退款的2张图片
		var ErrorLogInfo models.MaRobotErrorLogPallets
		db.Model(&models.MaRobotErrorLogPallets{}).
			// Where("out_trade_no = ?", v.OutTradeNo).
			Where("out_trade_no in (?, ?)", v.OutTradeNo, v.ChildOutTradeNo).
			Order("add_time desc").
			Take(&ErrorLogInfo)
		if ErrorLogInfo.ID != "" {
			item.OriginaImagePath = ErrorLogInfo.InputImagePath
			item.InputImagePath = ErrorLogInfo.OriginaImagePath
		}
		resData = append(resData, item)
	}
	return resData, count, totalCount, nil
}

// RefundOrderAnalyze 退款订单分析
func (o OrderService) RefundOrderAnalyze(
	ctx context.Context,
	merchantID,
	userID string,
	page, limit int,
	salesID string, // 设备ID
	shopIDList []string, // 商户列表
	startTime, endTime, // 开始和结束时间
	areas1, areas2, // 所在地区
	areas3 string, // 所在位置
) (
	res []RefundOrderList,
	err error,
) {
	db := mysql.NewDB()
	// 以商户为分组，查询每个商户的退款数量、退款金额、退款原因
	data, err1 := o.GetRefundAnalyzeOfMerchant(
		ctx, db, merchantID, userID,
		page, limit,
		salesID,
		shopIDList,
		startTime,
		endTime,
		areas1, areas2, areas3,
	)
	if err1 != nil {
		err = err1
		return
	}
	// grpc获取当前商户下的所有用户列表，用于查询退款人名称
	SystemAdminClient, clientErr := client.GetSystemAdminClient()
	if clientErr != nil {
		err = clientErr
		return
	}
	var req system_admin.UserListReq
	userListResult, err1 := SystemAdminClient.UserList(ctx, &req)
	if userListResult == nil || err1 != nil || userListResult.Code != 0 {
		logx.Error(ctx, "userListResult error", logx.Any("userListResult", userListResult), logx.Any("err", err1.Error()))
		return
	}
	var userNameList = make(map[string]string)
	for _, v := range userListResult.List {
		userNameList[v.Id] = v.Username
	}

	resData := []RefundOrderList{}
	for _, v := range data {
		var item RefundOrderList
		countData := common.Order{}.CountDataOldToNew(v.CountData)
		if len(countData) > 0 {
			item.GoodsName = countData[0].GoodsName
		}
		item.ErrorCode = v.ErrorCode
		if v.PaymentID == 6 {
			item.OutTradeNo = v.EndOutTradeNo
		} else {
			item.OutTradeNo = v.OutTradeNo
		}

		item.OutTradeNo = v.OutTradeNo
		item.ChildOutTradeNo = v.ChildOutTradeNo
		addTimeFormated, _ := models.DateTime(v.AddTime).MarshalDateTime()
		item.AddTime = string(addTimeFormated)
		modifyTimeFormated, _ := models.DateTime(v.ModifyTime).MarshalDateTime()
		item.ModifyTime = string(modifyTimeFormated)
		item.PaymentID = v.PaymentID // 0-微信 1-支付宝 2-支付宝人脸 3-刷卡 4-好邻居 5-兑换码
		item.PaymentAmount = v.PaymentAmount
		item.UserName = v.UserName
		item.UserID = v.UserID
		item.RobotName = v.RobotName
		item.SalesID = v.SalesID
		item.ShopName = v.ShopName
		item.Univalent = v.Univalent
		item.ContainerNum = v.ContainerNum
		item.SalesNum = 1
		item.Remarks = v.Remarks
		item.Reason = v.Reason
		item.RefundTime = v.RefundTime
		item.OrderStatus = v.OrderStatus

		// 人工退款原因显示 -- add in 2022-02-11
		if item.Reason == "0" || item.Reason == "1" || item.Reason == "2" || item.Reason == "3" {
			item.Reason = ""
		}
		RefundRemarks := ""
		if v.RefundRemarks != "" {
			RefundRemarks = "(退款原因:" + v.RefundRemarks + ")"
		}
		item.Remarks = v.Remarks + RefundRemarks
		item.RefundUserID = v.RefundUserID
		if v.RefundUserID != "" && userNameList[v.RefundUserID] != "" {
			item.RefundUserName = userNameList[v.RefundUserID]
		}
		// 出货失败时，显示上报的错误信息 -- add in 2022-02-10
		RefundResult := "" // 定义退款原因
		if v.OrderStatus == 6 || v.OrderStatus == 5 {
			// （ 获取退款操作指南 ）： 先从本订单表获取，再从错误表获取
			if v.ErrorCode == "" {
				// 细分逻辑： 如果是自动退款，正常会有错误码上报，如果是手动退款，则没有错误码上报
				// 自动退款
				if v.Remarks != "出货失败_退款成功" && v.Remarks != "出货失败_退款成功<商品已出弹簧>" && v.Remarks != "出货失败_退款，保存失败" && v.Remarks != "支付页面 订单结束" {
					// 0-网络异常订单支付未出货 1-货品出错 2-商品质量问题 3-其他
					switch v.Remarks {
					case "0":
						v.Remarks = "网络异常订单支付未出货"
					case "1":
						v.Remarks = "货品出错"
					case "2":
						v.Remarks = "商品质量问题"
					case "3":
						v.Remarks = "其他"
					}
					RefundResult = "【手动退款】：" + v.Remarks
				} else if v.Remarks == "支付页面 订单结束" {
					// 超时支付或者卡点支付
					RefundResult = "用户超时支付或取消出货"
				}
				// }
			} else {
				// - 多商品出货失败，后续商品暂停出货，完善退款原因.
				if v.ErrorCode == "0" && v.ChildOutTradeNo != "" {
					// 判断是否属于多商品出货失败，后续未出货的情况
					myorderData := []models.MaOrder{}
					myorderDataCount := db.Model(&models.MaOrder{}).
						Where("out_trade_no = ?", v.OutTradeNo).
						Where("error_code > ?", 0).
						Order("out_trade_no desc").
						Find(&myorderData).RowsAffected
					if myorderDataCount > 0 {
						RefundResult = "多商品出货失败，后续商品暂停出货"
					}
				}
				if RefundResult == "" {
					RefundResult = getRefundMethod(v.ErrorCode)
				}
			}
			if RefundResult == "" {
				RefundResult = "未知异常"
			}
			if len(countData) > 0 {
				item.Remarks = countData[0].ContainerNum + "货道_" + RefundResult
			}
		}

		// 获取退款的2张图片
		var ErrorLogInfo models.MaRobotErrorLogPallets
		db.Model(&models.MaRobotErrorLogPallets{}).
			// Where("out_trade_no = ?", v.OutTradeNo).
			Where("out_trade_no in (?, ?)", v.OutTradeNo, v.ChildOutTradeNo).
			Order("add_time desc").
			Take(&ErrorLogInfo)
		if ErrorLogInfo.ID != "" {
			item.OriginaImagePath = ErrorLogInfo.InputImagePath
			item.InputImagePath = ErrorLogInfo.OriginaImagePath
		}
		resData = append(resData, item)
	}
	return resData, nil
}

/**
 *
 * 获取订单出货完成的备注信息
 */
func getRefundMethod(ErrorCode string) string {
	if errorMsg, ok := define.RefundErrCode[ErrorCode]; ok {
		return errorMsg
	} else {
		return ErrorCode + "错误编码不存在"
	}
}

// 后台订单的CountData string -- ContainerNum
type PushDataStruct struct {
	ClassID      string  `json:"ClassID"`      // class_id
	ContainerNum string  `json:"ContainerNum"` // container_num //: 10,
	ErrorCode    int     `json:"ErrorCode"`    // error_code    //: 15,
	Floor        int     `json:"Floor"`        // floor         //: 1,
	GoodsID      string  `json:"GoodsID"`      // goods_id      //: "391",
	GoodsName    string  `json:"GoodsName"`    // goods_name    //: "9",
	IsSuccess    int     `json:"IsSuccess"`    // is_success    //: 1,
	IsTurn       int     `json:"IsTurn"`       // is_turn       //: 1,
	OrderId      string  `json:"OrderId"`      // order_id      //: "1678878115546477",
	OrderType    int     `json:"OrderType"`    // order_type    //: 0,
	Univalent    float64 `json:"Univalent"`    // univalent
}

// 安卓传int - ContainerNum
type PushDataStructInt struct {
	ClassID      string  `json:"ClassID"`      // class_id
	ContainerNum int     `json:"ContainerNum"` // container_num //: 10,
	ErrorCode    int     `json:"ErrorCode"`    // error_code    //: 15,
	Floor        int     `json:"Floor"`        // floor         //: 1,
	GoodsID      string  `json:"GoodsID"`      // goods_id      //: "391",
	GoodsName    string  `json:"GoodsName"`    // goods_name    //: "9",
	IsSuccess    int     `json:"IsSuccess"`    // is_success    //: 1,
	IsTurn       int     `json:"IsTurn"`       // is_turn       //: 1,
	OrderId      string  `json:"OrderId"`      // order_id      //: "1678878115546477",
	OrderType    int     `json:"OrderType"`    // order_type    //: 0,  //0 代表普通购物类型
	Univalent    float64 `json:"Univalent"`    // univalent
}

// 【安卓】统一关闭订单入口
//
//	pushData数据结构
//	*	{
//	*		"ClassID":"0",			// 商品类别ID
//	*		"ContainerNum":0,		// 商品货道号
//	*		"ErrorCode":25,			// 错误吗
//	*		"Floor":0,				// 楼层数
//	*		"GoodsID":"407",		// 商品ID
//	*		"GoodsName":"tset0208",	// 商品名称
//	*		"IsSuccess":2,			// 是否成功 	0未出货  1出货成功 2 ：出货失败
//	*		"IsTurn":0,				// 弹簧是否转动 0没 	  1转动
//	*		"OrderId":"1678852615241000", // 订单号
//	*		"0rderType":0,			// 订单类型 	0购买订单 1赠送订单
//	*		"Univalent":0.01
//	*	}
func (o OrderService) CloseOrder(
	ctx context.Context,
	db *gorm.DB,
	robotID,
	salesID,
	outTradeNo string,
	endType string,
	popUp int,
	errorCode string,
	endRemarks, // 安卓传输过来的备注
	logMsg,
	pushData string,
) (
	err error,
) {
	var orderInfo schema.MyOrderInfo
	db.Model(&models.MaOrder{}).
		Where("out_trade_no = ?", outTradeNo).
		Select("*,sum(payment_amount) as total_payment_amount"). // 多订单号相同时，总金额需累加。
		Take(&orderInfo)
	if orderInfo.End == 1 {
		err = errorx.New("订单已结束", -1)
		return
	}
	// 出货类型判断
	if !lo.Contains([]string{"0", "1", "2"}, endType) {
		orderInfo.OrderStatus = 7 // 错误
		orderInfo.Remarks = endRemarks + "___" + endType
		return
	}
	orderInfo.Remarks = endRemarks

	// 查询机器信息
	RobotInfo, _ := dao.Robot{}.Detail(ctx, dal.Q, "", salesID)

	// 定义退款金额
	RefundFee := decimal.NewFromInt(0)
	// 获取PushData，对每个商品绑定的订单依个处理 （最大有3个）
	var pushDataList []PushDataStructInt
	json.Unmarshal([]byte(pushData), &pushDataList)

	// 判断订单是否属于加购订单，childOutTradeNo值不为空就是
	childOut := 0
	if orderInfo.ChildOutTradeNo != "" || len(pushDataList) > 1 {
		childOut = 1
	}

	// 取货码的情况清空取货信息
	redis.NewDB().Del(ctx, define.RedisPickupCode+salesID)

	if endType == "0" { // endType 0 出货失败
		// 禁用货道
		var ErrorContainers []string // 需禁用的货道
		if pushData == "" {          // -- 单商品，非多商品 ---
			if lo.Contains(ErrorCodeArr, errorCode) { // 通过获取订单中的商品信息来禁用货道
				var countData []PushDataStruct
				_ = json.Unmarshal([]byte(orderInfo.CountData), &countData)
				for _, v := range countData {
					ErrorContainers = append(ErrorContainers, v.ContainerNum)
				}
			}
		} else {
			for _, v := range pushDataList { // 根据传入的pushData来禁用出货失败的货道
				if lo.Contains(ErrorCodeArr, strconv.Itoa(v.ErrorCode)) {
					ErrorContainers = append(ErrorContainers, strconv.Itoa(v.ContainerNum))
				}
			}
		}
		// 禁用货道,写入禁用日志
		for _, containerNum := range ErrorContainers {
			db.Model(&models.MaRobotSalesInfo{}).
				Where("sales_id = ? and container_num = ? and container_status = 1", salesID, containerNum).
				Updates(map[string]interface{}{
					"container_use_status": 2,
				})
			var CloseContainnerLogData models.MaCloseContainnerLog
			CloseContainnerLogData.SalesID = salesID
			CloseContainnerLogData.ContainerNum, _ = strconv.Atoi(containerNum)
			CloseContainnerLogData.AddTime = time.Now()
			db.Model(&models.MaCloseContainnerLog{}).Create(&CloseContainnerLogData)
		}

		// 兑换码订单 清空兑换码信息
		if orderInfo.PaymentID == 5 {
			clearCode := db.Model(&models.MaRedemptionCode{}).
				Where("out_trade_no = ?", outTradeNo).
				Updates(map[string]interface{}{
					"exchange_value":   0,
					"sales_id":         "",
					"robot_name":       "",
					"usage_time":       0,
					"new_usage_time":   0,
					"out_trade_no":     "",
					"class_id":         "",
					"goods_id":         "",
					"redemption_state": 1,
				}).RowsAffected
			if clearCode == 0 {
				orderInfo.Remarks = endRemarks + "_兑换码已无效"
			} else {
				orderInfo.Remarks = endRemarks + "_兑换码已恢复"
			}
			orderInfo.OrderStatus = 5 // 出货失败
		} else {
			// 多个商品时，计算失败个数商品 -- 并且计算退款金额
			// 支付宝微信、聚合、大兴退款 都在这里
			if pushData != "" {
				// 计算退款金额  ------ 适用于加购逻辑
				// 禁用货道 	------ 适用于加购逻辑
				for _, v := range pushDataList {
					// OrderType == 0 代表普通购物类型，其中，加购也属于其中的一项
					if v.IsSuccess != 1 && v.OrderType == 0 {
						// 1. 计算总退款额（只针对非赠送订单进行退款）
						var orderInfoForRefundFee models.MaOrder
						txSub := db.Model(&models.MaOrder{}).
							Where("out_trade_no = ?", orderInfo.OutTradeNo)
						if v.OrderId != "" && outTradeNo != v.OrderId { // 子订单
							txSub = txSub.Where("child_out_trade_no = ?", v.OrderId)
						}
						orderCount := txSub.First(&orderInfoForRefundFee).RowsAffected
						if orderCount > 0 {
							RefundFee = RefundFee.Add(decimal.NewFromFloat(orderInfoForRefundFee.PaymentAmount))
						}
					}
				}
			} else {
				// 单个商品时，退款金额为当前订单的金额
				RefundFee = decimal.NewFromFloat(orderInfo.PaymentAmount)
			}

			// 【自动退款】 统一入口
			refundCode, refundErr := OrderRefund{}.RefundOrder(ctx, db, salesID, outTradeNo, orderInfo,
				RefundFee.InexactFloat64(), "0", "出货失败", "")
			if refundErr != nil {
				err = refundErr
				return
			}

			// 判断是否有子订单，如果有，则说明是加购订单。1.判断IsSuccess 2.判断isTurn 弹簧是否转动
			// 加购多订单模式
			if childOut == 1 {
				// 循环对个每个子订单依次处理
				for _, v := range pushDataList {
					// 为每个商品所属的订单设置状态,主要是设置退款的备注及订单状态2个字段的数据
					if v.OrderId != "" {
						var orderInfo1 schema.MyOrderInfo
						db.Model(&models.MaOrder{}).
							Where("child_out_trade_no = ?", v.OrderId).
							Where("out_trade_no = ?", orderInfo.OutTradeNo).
							Select("*,sum(payment_amount) as total_payment_amount").
							Take(&orderInfo1)
						if v.IsSuccess == 2 || v.IsSuccess == 0 { // 0未出货 2出货失败
							// 把当前商品所属的订单设置为出货失败 -- 里面的error_code也绑定到当前订单里面
							updateData1 := make(map[string]interface{})
							updateData1["error_code"] = v.ErrorCode
							// -------- 根据退款状态 -- 执行告警通知 ------------
							switch refundCode {
							case 0: // 退款失败
								updateData1["remarks"] = "出货失败_退款失败"
								orderInfo.OrderStatus = 5
							case 1: // 退款成功
								updateData1["remarks"] = "出货失败_退款成功"
								orderInfo.OrderStatus = 6
								// 推送消息
								alarmMsg := map[string]interface{}{
									"alarm_type": 1001, // 告警类型
									"robot_id":   RobotInfo.RobotName,
									"battery":    "",
									"content":    define.RefundErrCode[strconv.Itoa(v.ErrorCode)],
									"created_at": time.Now().Unix(),
								}
								jsonString, _ := json.Marshal(alarmMsg)
								rdb := redis.NewDB()
								rdb.LPush(ctx, define.RedisAlarmNotifyList, string(jsonString))
							case 2: // 退款失败，保存失败
								updateData1["remarks"] = "出货失败_退款，保存失败"
								orderInfo.OrderStatus = 6
							}
							// 如果弹簧转动，订单后面备注:出货失败_退款成功！<商品已出弹簧>
							if v.IsTurn == 1 {
								updateData1["remarks"] = updateData1["remarks"].(string) + "<商品已出弹簧>"
							}
							updateData1["order_status"] = orderInfo.OrderStatus
							updateData1["end"] = 1                  // 订单已关闭
							updateData1["modify_time"] = time.Now() // 订单更新时间
							// 更新订单信息
							if orderInfo1.ChildOutTradeNo != "" {
								db.Model(&models.MaOrder{}).
									Where("child_out_trade_no = ?", v.OrderId).
									Where("out_trade_no = ?", orderInfo.OutTradeNo).
									Updates(updateData1)
							} else {
								db.Model(&models.MaOrder{}).
									Where("out_trade_no = ?", v.OrderId).
									Updates(updateData1)
							}

							//  通知小程序出货失败
							if orderInfo.OrderSource == 1 {
								miniAppClient, miniAppErr := client.GetMiniAppClient()
								if miniAppErr == nil {
									miniAppClient.PickupNotify(ctx, &mini_app.PickupNotifyReq{
										OutTradeNo:      orderInfo.OutTradeNo,
										ChildOutTradeNo: v.OrderId,
										Status:          2, // 1成功 2失败
									})
								}
							}
						} else if v.IsSuccess == 1 { // 1出货成功
							orderInfo.OrderStatus = 4
							// 开始保存
							data := make(map[string]interface{})
							data["order_status"] = orderInfo.OrderStatus
							data["remarks"] = "出货成功"
							data["end"] = 1                  // 订单已关闭
							data["modify_time"] = time.Now() // 订单更新时间
							if orderInfo1.ChildOutTradeNo != "" {
								db.Model(&models.MaOrder{}).
									Where("child_out_trade_no = ?", v.OrderId).
									Where("out_trade_no = ?", orderInfo.OutTradeNo).
									Updates(&data)
							} else {
								db.Model(&models.MaOrder{}).
									Where("out_trade_no = ?", v.OrderId).
									Updates(&data)
							}
							//  通知小程序出货失败
							if orderInfo.OrderSource == 1 {
								miniAppClient, miniAppErr := client.GetMiniAppClient()
								if miniAppErr == nil {
									miniAppClient.PickupNotify(ctx, &mini_app.PickupNotifyReq{
										OutTradeNo:      orderInfo.OutTradeNo,
										ChildOutTradeNo: v.OrderId,
										Status:          1, // 1成功 2失败
									})
								}
							}
						}
						// 处理库存 【1出货成功 || 0未出货和2出货失败时，如果弹簧转动】
						if v.IsSuccess == 1 || ((v.IsSuccess == 2 || v.IsSuccess == 0) && v.IsTurn == 1) {
							// 库存 - 1
							o.DecreaseGoodsBySalesIdAndContainerNum(ctx, db, salesID, strconv.Itoa(v.ContainerNum))
							// 商品分类销量 + 1
							o.AddGoodsClassTotalSales(db, v.ClassID)
							// 商品销售量 + 1
							o.AddGoodsInfoTotalSales(db, v.GoodsID)
						} else {
							// 出货失败预留-1
							db.Model(&models.MaRobotSalesInfo{}).
								Where("sales_id = ? and container_num = ?", salesID, v.ContainerNum).
								Where("goods_reserve_inventory > 0").
								UpdateColumn("goods_reserve_inventory", gorm.Expr("goods_reserve_inventory - 1"))
						}
					}
				}
			} else if childOut == 0 {
				// 非加购
				orderInfo.ErrorCode = errorCode
				orderInfo.LogMsg = logMsg
				// 根据退款状态 -- 执行告警通知
				switch refundCode {
				// 退款失败
				case 0:
					orderInfo.Remarks = endRemarks + "_退款失败"
					orderInfo.OrderStatus = 5
				// 退款成功
				case 1:
					orderInfo.Remarks = endRemarks + "_退款成功"
					orderInfo.OrderStatus = 6
					// 推送消息
					alarmMsg := map[string]interface{}{
						"alarm_type": 1001, // 告警类型
						"robot_id":   RobotInfo.RobotName,
						"battery":    "",
						"content":    define.RefundErrCode[errorCode],
						"created_at": time.Now().Unix(),
					}
					jsonString, _ := json.Marshal(alarmMsg)
					rdb := redis.NewDB()
					rdb.LPush(ctx, define.RedisAlarmNotifyList, string(jsonString))
				// 退款，保存失败
				case 2:
					orderInfo.Remarks = endRemarks + "_退款，保存失败"
					orderInfo.OrderStatus = 6
				}
				orderInfo.End = 1                 // 订单已关闭
				orderInfo.ModifyTime = time.Now() // 订单更新时间
				db.Model(&models.MaOrder{}).
					Where("out_trade_no = ?", outTradeNo).
					Updates(&orderInfo)

				// 处理库存
				var CountData []schema.OrderGoodsDetailOld
				json.Unmarshal([]byte(orderInfo.CountData), &CountData)
				for _, v := range CountData {
					// 出货失败预留-1
					db.Model(&models.MaRobotSalesInfo{}).
						Where("sales_id = ? and container_num = ?", salesID, v.ContainerNum).
						Where("goods_reserve_inventory > 0").
						UpdateColumn("goods_reserve_inventory", gorm.Expr("goods_reserve_inventory - 1"))
				}

				//  通知小程序出货失败
				if orderInfo.OrderSource == 1 {
					miniAppClient, miniAppErr := client.GetMiniAppClient()
					if miniAppErr == nil {
						miniAppClient.PickupNotify(ctx, &mini_app.PickupNotifyReq{
							OutTradeNo: orderInfo.OutTradeNo,
							Status:     2, // 1成功 2失败
						})
					}
				}
			}
		}
	} else if endType == "1" { // endType 1 出货成功
		orderInfo.OrderStatus = 4 // 4 出货成功
		if childOut == 0 {        // 1.非加购订单，只处理当前一个订单即可
			var CountData []schema.OrderGoodsDetailOld
			json.Unmarshal([]byte(orderInfo.CountData), &CountData)
			for _, v := range CountData {
				// 库存-1
				o.DecreaseGoodsBySalesIdAndContainerNum(ctx, db, salesID, v.ContainerNum)
				// 商品分类销量+1
				o.AddGoodsClassTotalSales(db, v.ClassID)
				// 商品销售量+1
				o.AddGoodsInfoTotalSales(db, v.GoodsID)
			}
			orderInfo.End = 1                 // 订单已关闭
			orderInfo.ModifyTime = time.Now() // 订单更新时间
			orderInfo.Remarks = endRemarks
			// 执行更新 === 区别：这里是一次更新主订单没问题，但不能更新多个。
			db.Model(&models.MaOrder{}).
				Where("out_trade_no = ?", outTradeNo).
				Updates(&orderInfo)
		} else if childOut == 1 { // 2. 第二种情况,childOutTradeNo有值，则需要对多个订单进行扣减库存
			// 循环对个每个子订单依次处理为【订单已关闭】
			for _, v := range pushDataList {
				var orderInfo1 schema.MyOrderInfo
				db.Model(&models.MaOrder{}).
					Where("child_out_trade_no = ?", v.OrderId).
					Where("out_trade_no = ?", orderInfo.OutTradeNo).
					Select("*,sum(payment_amount) as total_payment_amount").
					Take(&orderInfo1)
				// 开始保存
				data := make(map[string]interface{})
				data["order_status"] = orderInfo.OrderStatus
				data["remarks"] = endRemarks
				data["end"] = 1                  // 订单已关闭
				data["modify_time"] = time.Now() // 订单更新时间
				// 子单模式
				if orderInfo1.ChildOutTradeNo != "" {
					db.Model(&models.MaOrder{}).
						Where("child_out_trade_no = ?", v.OrderId).
						Where("out_trade_no = ?", orderInfo.OutTradeNo).
						Updates(data)
				} else {
					db.Model(&models.MaOrder{}).
						Where("out_trade_no = ?", v.OrderId).
						Updates(data)
				}
				// == 扣减库存 ===
				// 库存 - 1
				o.DecreaseGoodsBySalesIdAndContainerNum(ctx, db, salesID, strconv.Itoa(v.ContainerNum))
				// 商品分类销量 + 1
				o.AddGoodsClassTotalSales(db, v.ClassID)
				// 商品销售量 + 1
				o.AddGoodsInfoTotalSales(db, v.GoodsID)
			}
		}
		//  通知小程序出货失败
		if orderInfo.OrderSource == 1 {
			miniAppClient, miniAppErr := client.GetMiniAppClient()
			if miniAppErr == nil {
				miniAppClient.PickupNotify(ctx, &mini_app.PickupNotifyReq{
					OutTradeNo: orderInfo.OutTradeNo,
					Status:     1, // 1成功 2失败
				})
			}
		}
	} else if endType == "2" { // endType 2 订单结束
		// 判断订单是否支付，支付则不允许改状态
		if orderInfo.OrderStatus != 2 && orderInfo.OrderStatus != 6 { // 2 已支付 6 已退款
			orderInfo.OrderStatus = 3 // 未支付
		}
		if childOut == 0 { // 非加购
			// 未出货成功但商品已出弹簧 --- 只针对一个商品的情况，一个ErrorCode对应一个商品
			if popUp == 1 {
				var CountData []schema.OrderGoodsDetailOld
				json.Unmarshal([]byte(orderInfo.CountData), &CountData)
				for _, v := range CountData {
					// 库存 - 1
					o.DecreaseGoodsBySalesIdAndContainerNum(ctx, db, salesID, v.ContainerNum)
					// 商品分类销量 + 1
					o.AddGoodsClassTotalSales(db, v.ClassID)
					// 商品销售量 + 1
					o.AddGoodsInfoTotalSales(db, v.GoodsID)
				}
				orderInfo.Remarks += "<商品已出弹簧>"
			}
			orderInfo.End = 1                 // 订单已关闭
			orderInfo.ModifyTime = time.Now() // 订单更新时间
			orderInfo.Remarks = endRemarks    // 订单备注
			// 执行更新 === 区别：这里是一次更新主订单没问题，但不能更新多个。
			db.Model(&models.MaOrder{}).
				Where("out_trade_no = ?", outTradeNo).
				Updates(&orderInfo)
		} else if childOut == 1 {
			// 循环对个每个子订单依次处理为【订单已关闭】
			for _, v := range pushDataList {
				var orderInfo1 schema.MyOrderInfo
				db.Model(&models.MaOrder{}).
					Where("child_out_trade_no = ?", v.OrderId).
					Where("out_trade_no = ?", orderInfo.OutTradeNo).
					Select("*,sum(payment_amount) as total_payment_amount").
					Take(&orderInfo1)
				data := make(map[string]interface{})
				data["order_status"] = orderInfo.OrderStatus
				data["remarks"] = endRemarks
				data["end"] = 1                  // 订单已关闭
				data["modify_time"] = time.Now() // 订单更新时间
				// 子单模式
				if orderInfo1.ChildOutTradeNo != "" {
					db.Model(&models.MaOrder{}).
						Where("child_out_trade_no = ?", v.OrderId).
						Where("out_trade_no = ?", orderInfo.OutTradeNo).
						Updates(&data)
				} else {
					db.Model(&models.MaOrder{}).
						Where("out_trade_no = ?", v.OrderId).
						Updates(&data)
				}
			}
		}
	}
	// 释放子码，随便找一个坑位
	if endType == "1" {
		// 把（使用中）3改为（已使用）2
		if orderInfo.CouponCode != "" {
			var couponChildData models.MaCouponChildCode
			couponChildData.Status = 2              // 已使用
			couponChildData.OutTradeNo = outTradeNo // 关联订单号
			db.Model(&models.MaCouponChildCode{}).
				Where("code = ?", orderInfo.CouponCode). // 优惠码
				Where("status = 3").                     // 3 使用中。 优惠码状态 1待使用2已使用3使用中
				Updates(&couponChildData).
				Limit(1)
		}
	} else {
		// 把（使用中）3改为（待使用）1
		if orderInfo.CouponCode != "" {
			db.Model(&models.MaCouponChildCode{}).
				Where("code = ?", orderInfo.CouponCode). // 优惠码
				Where("status = 3").                     // 3 使用中。 优惠码状态 1待使用2已使用3使用中
				Update("status", 1).
				Limit(1)
		}
	}
	// 通知小程序修改订单状态
	miniAppClient, miniAppErr := client.GetMiniAppClient()
	if miniAppErr == nil {
		miniAppClient.OrderStatusNotify(context.Background(), &mini_app.OrderStatusNotifyReq{
			OutTradeNo:  orderInfo.OutTradeNo,
			OrderStatus: int32(orderInfo.OrderStatus),
		})
	}
	return
}

//	------------------- 对于抽奖订单。1.要么出货成功。2.要么出货失败。 -------------------
//
// 第1种情况：endtype == 0  出货失败
// 第2种情况：endtype == 1  出货成功
// CloseLotteryOrder 关闭抽奖订单
func (o OrderService) CloseLotteryOrder(
	ctx context.Context, db *gorm.DB,
	robotID,
	salesID,
	outTradeNo string, // 抽奖订单号
	endType string,
	popUp int,
	errorCode string,
	endRemarks, // 安卓传输过来的备注
	logMsg,
	pushData string,
) (
	err error,
) {
	var orderInfo schema.MyOrderInfo
	// 修改前：只查单个订单，一条的数据
	// 修改后：多返回一个字段，total_payment_amount。多订单号相同时，金额需累加。
	db.Model(&models.MaLotteryOrder{}).
		Where("out_trade_no = ?", outTradeNo).
		Select("*,sum(payment_amount) as total_payment_amount").
		Take(&orderInfo)
	if orderInfo.End == 1 {
		err = errorx.New("订单已结束", -1)
		return
	}

	orderInfo.Remarks = endRemarks

	// 获取PushData，对每个商品绑定的订单依个处理 （最大有3个）
	var pushDataList []PushDataStructInt
	json.Unmarshal([]byte(pushData), &pushDataList)

	// 判断订单是否属于加购订单，childOutTradeNo值不为空就是
	childOut := 0
	if orderInfo.ChildOutTradeNo != "" || len(pushDataList) > 1 {
		childOut = 1
	}
	// -------- 出货失败 --------
	if endType == "0" {
		// -- 单商品，非多商品 ---
		if pushData == "" {
			// 处理货道状态
			if lo.Contains(ErrorCodeArr, errorCode) {
				// 通过获取商品信息来禁用货道
				var countData []PushDataStruct
				_ = json.Unmarshal([]byte(orderInfo.CountData), &countData)
				for _, v := range countData {
					// 禁用货道,写入禁用日志
					db.Model(&models.MaRobotSalesInfo{}).
						Where("sales_id = ? and container_num = ? and container_status = 1", salesID, v.ContainerNum).
						Updates(map[string]interface{}{
							"container_use_status": 2,
						})
					var CloseContainnerLogData models.MaCloseContainnerLog
					CloseContainnerLogData.SalesID = salesID
					CloseContainnerLogData.ContainerNum, _ = strconv.Atoi(v.ContainerNum)
					CloseContainnerLogData.AddTime = time.Now()
					db.Model(&models.MaCloseContainnerLog{}).Create(&CloseContainnerLogData)
				}
			}
		}
		// 多个商品时，计算失败个数商品 -- 并且计算退款金额
		// 支付宝微信、聚合、大兴退款 都在这里
		// ======  A -- 如果是PushData ========
		if pushData != "" {
			// 转换成数组
			var pushDataList []PushDataStructInt
			json.Unmarshal([]byte(pushData), &pushDataList)
			// 计算退款金额  ------ 适用于加购逻辑
			// 禁用货道 	------ 适用于加购逻辑
			for _, v := range pushDataList {
				// OrderType == 0 代表普通购物类型，其中，加购也属于其中的一项
				if v.IsSuccess != 1 && v.OrderType == 0 {
					// 2. 禁用货道。根据关闭订单中返回的商品信息来禁用出货失败的货道
					if lo.Contains(ErrorCodeArr, strconv.Itoa(v.ErrorCode)) {
						// 写入禁用货道日志
						db.Model(&models.MaRobotSalesInfo{}).
							Where("sales_id = ? and container_num = ? and container_status = 1", salesID, v.ContainerNum).
							Updates(map[string]interface{}{
								"container_use_status": 2,
							})
						var CloseContainnerLogData models.MaCloseContainnerLog
						CloseContainnerLogData.SalesID = salesID
						CloseContainnerLogData.ContainerNum = v.ContainerNum
						CloseContainnerLogData.AddTime = time.Now()
						db.Model(&models.MaCloseContainnerLog{}).Create(&CloseContainnerLogData)
					}
				}
			}
		}

		// 判断是否有子订单，如果有，则说明是加购订单。1.判断IsSuccess 2.判断isTurn 弹簧是否转动
		// 加购多订单模式
		if childOut == 1 {

			// 查询机器信息
			var RobotInfo models.MaRobot
			db.Model(&models.MaRobot{}).
				Where("sales_id = ? and deleted = 0", salesID).
				Take(&RobotInfo)

			// 循环对个每个子订单依次处理
			for _, v := range pushDataList {
				// 为每个商品所属的订单设置状态,主要是设置退款的备注及订单状态2个字段的数据
				if v.OrderId != "" && (v.IsSuccess == 2 || v.IsSuccess == 0) {
					//========  出货失败 === 判断弹簧是否转动 =====
					// 把当前商品所属的订单设置为出货失败 -- 里面的error_code也绑定到当前订单里面
					var orderInfo1 schema.MyOrderInfo
					db.Model(&models.MaLotteryOrder{}).
						Where("child_out_trade_no = ?", v.OrderId).
						Where("out_trade_no = ?", orderInfo.OutTradeNo).
						Select("*,sum(payment_amount) as total_payment_amount").
						Take(&orderInfo1)
					// 保存订单信息
					updateData1 := make(map[string]interface{})
					updateData1["error_code"] = v.ErrorCode
					updateData1["order_status"] = 6 // 更新订单状态
					updateData1["remarks"] = "抽奖订单，出货失败"
					updateData1["end"] = 1                  // 订单已关闭
					updateData1["modify_time"] = time.Now() // 订单更新时间
					// 如果弹簧转动，需要扣减当前商品的库存。并且订单后面备注:出货失败_退款成功！<商品已出弹簧>
					if v.IsTurn == 1 {
						updateData1["remarks"] = updateData1["remarks"].(string) + "<商品已出弹簧>"
						// 库存 - 1
						o.DecreaseGoodsBySalesIdAndContainerNum(ctx, db, salesID, strconv.Itoa(v.ContainerNum))
						// 商品分类销量 + 1
						o.AddGoodsClassTotalSales(db, v.ClassID)
						// 商品销售量 + 1
						o.AddGoodsInfoTotalSales(db, v.GoodsID)
					}

					if orderInfo1.ChildOutTradeNo != "" {
						db.Model(&models.MaLotteryOrder{}).
							Where("child_out_trade_no = ?", v.OrderId).
							Where("out_trade_no = ?", orderInfo.OutTradeNo).
							Updates(updateData1)
					} else {
						db.Model(&models.MaLotteryOrder{}).
							Where("out_trade_no = ?", v.OrderId).
							Updates(updateData1)
					}
				} else if v.OrderId != "" && v.IsSuccess == 1 { // 当前商品为出货成功
					//  ---- 出货成功 -------
					var orderInfo2 schema.MyOrderInfo
					db.Model(&models.MaLotteryOrder{}).
						Where("child_out_trade_no = ?", v.OrderId).
						Where("out_trade_no = ?", orderInfo.OutTradeNo).
						Select("*,sum(payment_amount) as total_payment_amount").
						Take(&orderInfo2)
					// 开始保存
					data := make(map[string]interface{})
					data["order_status"] = 4
					data["remarks"] = "出货成功"
					data["end"] = 1                  // 订单已关闭
					data["modify_time"] = time.Now() // 订单更新时间
					if orderInfo2.ChildOutTradeNo != "" {
						db.Model(&models.MaLotteryOrder{}).
							Where("child_out_trade_no = ?", v.OrderId).
							Where("out_trade_no = ?", orderInfo.OutTradeNo).
							Updates(&data)
					} else {
						db.Model(&models.MaLotteryOrder{}).
							Where("out_trade_no = ?", v.OrderId).
							Updates(&data)
					}
					// == 扣减库存 ===
					// 库存 - 1
					o.DecreaseGoodsBySalesIdAndContainerNum(ctx, db, salesID, strconv.Itoa(v.ContainerNum))
					// 商品分类销量 + 1
					o.AddGoodsClassTotalSales(db, v.ClassID)
					// 商品销售量 + 1
					o.AddGoodsInfoTotalSales(db, v.GoodsID)
				}
			}
		} else if childOut == 0 {
			// 非加购
			orderInfo.ErrorCode = errorCode
			orderInfo.LogMsg = logMsg
			orderInfo.Remarks = "抽奖订单，出货失败"
			orderInfo.OrderStatus = 6
			orderInfo.End = 1                 // 订单已关闭
			orderInfo.ModifyTime = time.Now() // 订单更新时间
			db.Model(&models.MaLotteryOrder{}).Where("out_trade_no = ?", outTradeNo).Updates(&orderInfo)
		}
	} else if endType == "1" {
		// -------- 出货成功 --------
		// 1.非加购订单，只处理当前一个订单即可

		// 2. 第二种情况,childOutTradeNo有值，则需要对多个订单进行扣减库存
		// 非加购
		if childOut == 0 {
			// 出货成功
			orderInfo.OrderStatus = 4
			var CountData []schema.OrderGoodsDetailOld
			json.Unmarshal([]byte(orderInfo.CountData), &CountData)
			for _, v := range CountData {
				// 库存-1
				o.DecreaseGoodsBySalesIdAndContainerNum(ctx, db, salesID, v.ContainerNum)
				// 商品分类销量+1
				o.AddGoodsClassTotalSales(db, v.ClassID)
				// 商品销售量+1
				o.AddGoodsInfoTotalSales(db, v.GoodsID)
			}
			orderInfo.End = 1                 // 订单已关闭
			orderInfo.ModifyTime = time.Now() // 订单更新时间
			orderInfo.Remarks = endRemarks
			// 执行更新 === 区别：这里是一次更新主订单没问题，但不能更新多个。
			db.Model(&models.MaLotteryOrder{}).
				Where("out_trade_no = ?", outTradeNo).
				Updates(&orderInfo)
		} else if childOut == 1 {
			// 循环对个每个子订单依次处理为，
			for _, v := range pushDataList {
				// 全部备注为订单已结束
				var orderInfo1 schema.MyOrderInfo
				db.Model(&models.MaLotteryOrder{}).
					Where("child_out_trade_no = ?", v.OrderId).
					Where("out_trade_no = ?", orderInfo.OutTradeNo).
					Select("*,sum(payment_amount) as total_payment_amount").
					Take(&orderInfo1)
				// 开始保存
				data := make(map[string]interface{})
				data["order_status"] = 4
				data["remarks"] = endRemarks
				data["end"] = 1                  // 订单已关闭
				data["modify_time"] = time.Now() // 订单更新时间
				// 子单模式
				if orderInfo1.ChildOutTradeNo != "" {
					db.Model(&models.MaLotteryOrder{}).
						Where("child_out_trade_no = ?", v.OrderId).
						Where("out_trade_no = ?", orderInfo.OutTradeNo).
						Updates(data)
				} else {
					db.Model(&models.MaLotteryOrder{}).
						Where("out_trade_no = ?", v.OrderId).
						Updates(data)
				}
				// == （奖品）扣减库存 ===
				// 库存 - 1
				o.DecreaseGoodsBySalesIdAndContainerNum(ctx, db, salesID, strconv.Itoa(v.ContainerNum))
				// 商品分类销量 + 1
				o.AddGoodsClassTotalSales(db, v.ClassID)
				// 商品销售量 + 1
				o.AddGoodsInfoTotalSales(db, v.GoodsID)
			}
		}
	} else if endType == "2" {
		// 订单结束
		// 3.判断订单是否支付，支付则不允许改状态
		if orderInfo.OrderStatus == 2 {
			orderInfo.OrderStatus = 2 // 已支付
		} else if orderInfo.OrderStatus == 6 {
			orderInfo.OrderStatus = 6 // 已退款
		} else { // 其他订单状态时关闭则改为 3未支付
			orderInfo.OrderStatus = 3 // 未支付
		}
		// 非加购
		if childOut == 0 {
			// 未出货成功但商品已出弹簧 --- 只针对一个商品的情况，一个ErrorCode对应一个商品
			if endType != "1" && popUp == 1 {
				var CountData []schema.OrderGoodsDetailOld
				json.Unmarshal([]byte(orderInfo.CountData), &CountData)
				for _, v := range CountData {
					// 库存 - 1
					o.DecreaseGoodsBySalesIdAndContainerNum(ctx, db, salesID, v.ContainerNum)
					// 商品分类销量 + 1
					o.AddGoodsClassTotalSales(db, v.ClassID)
					// 商品销售量 + 1
					o.AddGoodsInfoTotalSales(db, v.GoodsID)
				}
				orderInfo.Remarks += "<商品已出弹簧>"
			}
			orderInfo.End = 1                 // 订单已关闭
			orderInfo.ModifyTime = time.Now() // 订单更新时间
			orderInfo.Remarks = endRemarks    // 订单备注
			// 执行更新 === 区别：这里是一次更新主订单没问题，但不能更新多个。
			db.Model(&models.MaLotteryOrder{}).
				Where("out_trade_no = ?", outTradeNo).
				Updates(&orderInfo)
		} else if childOut == 1 {
			// 循环对个每个子订单依次处理为，
			for _, v := range pushDataList {
				// 全部备注为订单已结束
				var orderInfo1 schema.MyOrderInfo
				db.Model(&models.MaLotteryOrder{}).
					Where("child_out_trade_no = ?", v.OrderId).
					Where("out_trade_no = ?", orderInfo.OutTradeNo).
					Select("*,sum(payment_amount) as total_payment_amount").
					Take(&orderInfo1)
				// 开始保存
				data := make(map[string]interface{})
				// updateData3["order_status"] = 3 ---- bug 修复时间：23.8.29
				data["order_status"] = orderInfo.OrderStatus
				data["remarks"] = endRemarks
				data["end"] = 1                  // 订单已关闭
				data["modify_time"] = time.Now() // 订单更新时间
				// 子单模式
				if orderInfo1.ChildOutTradeNo != "" {
					db.Model(&models.MaLotteryOrder{}).
						Where("child_out_trade_no = ?", v.OrderId).
						Where("out_trade_no = ?", orderInfo.OutTradeNo).
						Updates(&data)
				} else {
					db.Model(&models.MaLotteryOrder{}).
						Where("out_trade_no = ?", v.OrderId).
						Updates(&data)
				}
			}
		}
	} else {
		orderInfo.OrderStatus = 7 // 错误
	}
	return
}

// DecreaseGoodsBySalesIdAndContainerNum 库存-1
func (o OrderService) DecreaseGoodsBySalesIdAndContainerNum(ctx context.Context, db *gorm.DB, salesID string, ContainerNum string) {
	// 出货成功 预留-1 库存-1
	db.Model(&models.MaRobotSalesInfo{}).
		Where("sales_id = ? and container_num = ?", salesID, ContainerNum).
		Where("goods_reserve_inventory > 0").
		UpdateColumn("goods_reserve_inventory", gorm.Expr("goods_reserve_inventory - 1"))
	db.Model(&models.MaRobotSalesInfo{}).
		Where("sales_id = ? and container_num = ?", salesID, ContainerNum).
		Where("goods_inventory > 0").
		UpdateColumn("goods_inventory", gorm.Expr("goods_inventory - 1"))

	var RobotInfo models.MaRobot
	db.Model(&models.MaRobot{}).Where("sales_id = ?", salesID).Take(&RobotInfo)
	// 获取当前机器的总库存和剩余库存
	var inventory struct {
		LastInventory  float64 `json:"last_inventory"`
		TotalInventory float64 `json:"total_inventory"`
	}
	db.Model(&models.MaRobotSalesInfo{}).
		Select("sum(goods_inventory) as last_inventory, sum(goods_max_inventory) as total_inventory").
		Where("container_status = ? AND container_use_status = ? AND sales_id = ?", 1, 1, salesID).
		Take(&inventory)
	if inventory.TotalInventory != 0 && inventory.LastInventory != 0 {
		if inventory.LastInventory/inventory.TotalInventory < 0.3 {
			// 执行低库存通知
			alarmMsg := map[string]interface{}{
				"alarm_type": 1002, // 告警类型
				"robot_id":   RobotInfo.RobotName,
				"battery":    "",
				"created_at": time.Now().Unix(),
			}
			jsonString, _ := json.Marshal(alarmMsg)
			rdb := redis.NewDB()
			rdb.LPush(ctx, define.RedisAlarmNotifyList, string(jsonString))
		}
	}
}

// 商品分类销量 + 1
func (o OrderService) AddGoodsClassTotalSales(db *gorm.DB, classID string) {
	if classID == "" {
		return
	}
	db.Model(&models.MaGoodsClass{}).
		Where("class_id = ?", classID).
		UpdateColumn("total_sales", gorm.Expr("total_sales + 1"))
}

// 商品销量 + 1
func (o OrderService) AddGoodsInfoTotalSales(db *gorm.DB, goodsID string) {
	db.Model(&models.MaGoodsInfo{}).
		Where("goods_id = ?", goodsID).
		UpdateColumn("total_sales", gorm.Expr("total_sales + 1"))
}

// UpdateOrderPaymentSuccessful 更新成功的订单
func (o OrderService) UpdateOrderPaymentSuccessful(ctx context.Context, db *gorm.DB, OutTradeNo, EndOutTradeNo, EndOrderResult, PaymentID string) (boolean bool) {
	var orderInfo models.MaOrder
	db.Model(&models.MaOrder{}).
		Where("out_trade_no = ?", OutTradeNo).
		Take(&orderInfo)
	if orderInfo.MerchantID == "" {
		return false
	}
	if orderInfo.OrderStatus == 2 {
		return true
	}
	// 已经确认支付结果的不再更新订单支付状态
	if orderInfo.OrderStatus != 3 && orderInfo.OrderStatus != 1 {
		return false
	}
	db.Model(&models.MaOrder{}).
		Where("out_trade_no = ?", OutTradeNo).Updates(map[string]interface{}{
		"end_out_trade_no": EndOutTradeNo,
		"order_status":     2, // 支付成功
		"payment_id":       PaymentID,
		"modify_time":      time.Now(),
	})
	return true
}

type ExportOrderTotalCount struct {
	TotalPaymentAmount float64 `json:"total_payment_amount"`
	TotalActualAmount  float64 `json:"total_actual_amount"`
}

// ExportOrderList 导出订单列表
func (o OrderService) ExportOrderList(
	ctx context.Context,
	db *gorm.DB,
	merchantID string,
	limit int,
	salesID string, shopIDList []string, payStatus int, orderStatus string, refundType *int,
	goodsName string, start, end int64, paymentID, outTradeNo,
	areas1, areas2, areas3 string,
	orderID string,
) (
	data []OrderList,
	count int64,
	totalCount ExportOrderTotalCount,
	err error,
) {
	tx := db.Table(models.MaOrder{}.TableName() + " as o")
	var startTime, endTime string

	// 订单状态筛选，支持多个条件
	if orderStatus != "" {
		// 新增状态检索： 检索支付成功或者退款但是状态未结束的订单 (order_status ==2 或者 ==6) and  end ==0
		if orderStatus == "1001" {
			// 注：这个状态只能单独搜索
			tx.Where("o.order_status in (?) and o.end = ?", "2,6", 0)
		} else {
			tx.Where("o.order_status in (?)", strings.Split(orderStatus, ","))
		}
	}

	// 退款类型筛选
	if refundType != nil {
		if *refundType == 0 { // 手动退款
			tx.Where("(o.remarks <> '出货失败_退款成功') or (o.remarks <> '出货失败_退款成功<商品已出弹簧>') or (o.remarks <> '出货失败_退款，保存失败')")
		} else if *refundType == 1 { // 自动退款
			tx.Where("o.remarks like '%出货失败%'")
		} else {
			err = errorx.New("退款类型错误", -1)
			return
		}
	}

	// 商户筛选
	if merchantID != "1" || len(shopIDList) != 0 {
		var merchantIDs []string
		merchantIDs, err = common.Order{}.GetMerchantChildren(ctx, merchantID, shopIDList)
		if err != nil {
			return
		}
		tx.Where("o.merchant_id in (?)", merchantIDs)
	}

	// 日期检索
	if start != 0 && end != 0 {
		startTime = carbon.CreateFromTimestamp(start).ToDateTimeString()
		endTime = carbon.CreateFromTimestamp(end).ToDateTimeString()
		tx.Where("o.add_time >= ? and o.add_time <= ?", startTime, endTime)
	}

	// 机器
	if salesID != "" {
		tx.Where("o.sales_id IN (?)", strings.Split(salesID, ","))
	}

	//支付方式
	if paymentID != "" {
		tx.Where("o.payment_id IN (?)", strings.Split(paymentID, ","))
	}

	//订单号
	if outTradeNo != "" {
		tx.Where("(o.out_trade_no like ? OR o.child_out_trade_no like ?)", outTradeNo+"%", outTradeNo+"%")
	}

	// 商品名称查询
	if goodsName != "" {
		goodsIDs := dao.Goods{}.GetGoodsIDByName(ctx, dal.Q, goodsName)
		tx = tx.Where("o.goods_id IN ?", goodsIDs)
	}

	// 地区筛选
	if areas1 != "" {
		tx.Where("o.areas1 in (?)", strings.Split(areas1, ","))
	}
	if areas2 != "" {
		tx.Where("o.areas2 in (?)", strings.Split(areas2, ","))
	}
	if areas3 != "" {
		tx.Where("o.areas3 in (?)", strings.Split(areas3, ","))
	}
	// 订单id范围
	if orderID != "" {
		tx.Where("o.order_id < ?", orderID)
	}
	// 新建会话，共享条件 ---- 1
	tx1 := tx.Session(&gorm.Session{})
	page := 1 // 因为通过end_time和order_id来推算起始位置，所以page固定为1
	if page == 1 && orderID == "" {
		// 计算总数
		tx1.Select("count(out_trade_no)").Take(&count)
		// 订单统计
		tx1.Where("o.order_status != 3").
			Select("sum(payment_amount) as total_payment_amount, sum(actual_amount) as total_actual_amount").
			Take(&totalCount)
	}
	orderList := []OrderList{}
	field := "o.order_id,o.robot_name, o.order_status, o.add_time, o.out_trade_no, o.child_out_trade_no, o.payment_id, o.areas3, o.modify_time, o.payment_amount, " +
		"o.discount_amount, o.actual_amount, o.deduction_amount, o.points_amount, o.points_value, o.deduction_limit, o.remarks, o.count_data, user.id as user_id, user.user_name"
	// 分页查询
	helper.Gorm{}.
		Paginate(tx1, page, limit).
		Joins("left join " + models.MaMerchantConfig{}.TableName() + " as user on o.merchant_id = user.id").
		Order("o.order_id desc").
		Select(field).
		Find(&orderList)

	// 初始化GRPC
	deviceClient, clientErr := client.GetDeviceAdminClient()
	if clientErr != nil {
		logx.Error(context.Background(), "grpc device client error", logx.Err(clientErr))
		return
	}

	// GRPC获取商场位置列表
	deviceReq := &devicec.LocationListReq{}
	deviceRes, err := deviceClient.LocationList(context.Background(), deviceReq)
	LocationMap := make(map[string]string)
	if deviceRes != nil && deviceRes.Code == 0 && err == nil {
		for _, v := range deviceRes.List {
			LocationMap[v.Id] = v.Name
		}
	}
	// 获取积分类型
	var PointsOrderIDs []string
	for k, v := range orderList {
		if v.PaymentID == 9 {
			PointsOrderIDs = append(PointsOrderIDs, v.OutTradeNo)
		}
		if v.OrderStatus == 3 {
			orderList[k].ActualAmount = 0
		}
	}
	OrderPointsList, _ := dao.OrderPoints{}.List(ctx, db, strings.Join(PointsOrderIDs, ","), 1)

	OrderPointsMap := make(map[string]string)
	for _, v := range OrderPointsList {
		if _, ok := define.PointsType[v.PointsID]; ok {
			OrderPointsMap[v.OutTradeNo] = define.PointsType[v.PointsID]
		}
	}

	var outTradeNoIDList []string
	for _, v := range orderList {
		outTradeNoIDList = append(outTradeNoIDList, v.OutTradeNo, v.ChildOutTradeNo)
	}
	// 获取退款的2张图片
	var ErrorLogList []models.MaRobotErrorLogPallets
	var ErrorLogMap = make(map[string]models.MaRobotErrorLogPallets)
	db.Model(&models.MaRobotErrorLogPallets{}).
		Where("out_trade_no in (?)", outTradeNoIDList).
		Group("out_trade_no"). // 同一订单只取最后一次的图片
		Order("out_trade_no desc").
		Find(&ErrorLogList)
	for _, v := range ErrorLogList {
		ErrorLogMap[v.OutTradeNo] = v
	}
	// 转换成标注的正确时间格式
	for k, v := range orderList {
		addTimeFormated, _ := models.DateTime(v.AddTime).MarshalDateTime()
		orderList[k].AddTimeFormated = string(addTimeFormated)
		modifyTimeFormated, _ := models.DateTime(v.ModifyTime).MarshalDateTime()
		orderList[k].ModifyTimeFormated = string(modifyTimeFormated)
		var countData []schema.OrderGoodsDetailOld
		json.Unmarshal([]byte(v.CountData), &countData)
		if len(countData) > 0 {
			orderList[k].GoodsName = countData[0].GoodsName
			orderList[k].ContainerNum = countData[0].ContainerNum
			orderList[k].Univalent = countData[0].Univalent
			orderList[k].ClassName = ""
		}

		if v.Areas3 != 0 {
			AreasID := strconv.Itoa(v.Areas3)
			if _, ok := LocationMap[AreasID]; ok {
				orderList[k].ShopName = LocationMap[AreasID]
			}
		}
		if ErrorLogInfo, ok := ErrorLogMap[v.OutTradeNo]; ok {
			orderList[k].OriginaImagePath = ErrorLogInfo.InputImagePath
			orderList[k].InputImagePath = ErrorLogInfo.OriginaImagePath
		} else if ErrorLogInfo, ok := ErrorLogMap[v.ChildOutTradeNo]; ok {
			orderList[k].OriginaImagePath = ErrorLogInfo.InputImagePath
			orderList[k].InputImagePath = ErrorLogInfo.OriginaImagePath
		}
		if v.PaymentID == 9 {
			if _, ok := OrderPointsMap[v.OutTradeNo]; ok {
				orderList[k].DiscountType = OrderPointsMap[v.OutTradeNo]
			}
		}
	}
	return orderList, count, totalCount, nil
}
